package org.easyspring.aop.aspect;

import lombok.Getter;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.easyspring.util.ValidationUtil;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 1.该类负责往被代理方法中添加横切逻辑
 * 2.使用的cglib，需要实现MethodInterceptor
 */
public class AspectListExecutor implements MethodInterceptor {
    //被代理的类
    private Class<?> targetClass;
    @Getter
    private List<AspectInfo> sortAspectInfoList;

    public AspectListExecutor(Class<?> targetClass, List<AspectInfo> aspectInfoList) {
        this.targetClass = targetClass;
        this.sortAspectInfoList = sortAspectInfoList(aspectInfoList);
    }

    /**
     * 按照order的值进行升序排序,确保order值小的aspect先被织入
     * @param aspectInfoList
     * @return
     */
    private List<AspectInfo> sortAspectInfoList(List<AspectInfo> aspectInfoList) {
        Collections.sort(aspectInfoList, new Comparator<AspectInfo>() {
            @Override
            public int compare(AspectInfo o1, AspectInfo o2) {
                return o1.getOrderIndex() - o2.getOrderIndex();
            }
        });
        return aspectInfoList;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object returnVal = null;
        if(ValidationUtil.isEmpty(sortAspectInfoList)){return returnVal;}
        //1.按照order的顺序升序执行完所有Aspect的before方法
        invokBeforeAdvices(method,args);
        try {
            //2.执行被代理类的方法
            returnVal = methodProxy.invokeSuper(proxy, args);
            //3.如果被代理方法正常返回，则按照order的顺序降序执行完所有Aspect的afterReturning方法
            returnVal = invokAfterReturningAdvices(method,args,returnVal);
        }catch (Exception e){
            //4.如果被代理方法抛出异常，则按照order的顺序降序执行所有Aspect的afterThrowing方法
            invokAfterThrowing(method,args,e);
        }
        return returnVal;
    }

    /**
     * 如果被代理方法抛出异常，则按照order的顺序降序执行所有Aspect的afterThrowing方法
     * @param method
     * @param args
     * @param e
     * @throws Throwable
     */
    private void invokAfterThrowing(Method method, Object[] args, Exception e) throws Throwable {
        for (int i = sortAspectInfoList.size() -1; i >=0 ; i--) {
            sortAspectInfoList.get(i).getAspectObj().afterThrowing(targetClass,method,args,e);
        }
    }

    /**
     * 如果被代理方法正常返回，则按照order的顺序降序执行完所有Aspect的afterReturning方法
     * @param method
     * @param args
     * @param returnVal
     * @return
     * @throws Throwable
     */
    private Object invokAfterReturningAdvices(Method method, Object[] args, Object returnVal) throws Throwable {
        Object result=null;
        for (int i = sortAspectInfoList.size()-1;i>=0;i--
             ) {
            result = sortAspectInfoList.get(i).getAspectObj().afterReturning(targetClass,method,args,returnVal);
        }
        return result;
    }

    /**
     * 按照order的顺序升序执行完所有Aspect的before方法
     * @param method
     * @param args
     * @throws Throwable
     */
    private void invokBeforeAdvices(Method method, Object[] args) throws Throwable {
        for (AspectInfo aspectInfo:sortAspectInfoList
             ) {
           aspectInfo.getAspectObj().before(targetClass,method,args);
        }
    }
}
